package com.cskaoyan.javse.oop3._2inner._7lambda._1basic;

/**
 * @description: lambda表达式的简化
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

import com.sun.org.apache.bcel.internal.generic.NEW;

/**
 * lambda表达式的简化,大的原则是不能有歧视
 * lambda表达式能够简化的前提是lambda表达式是功能接口的子类对象,而功能接口中只有一个必须要实现的抽象方法
 *
 * lambda表达式的语法:
 *      (形参列表) -> {
 *          //方法体
 *      }
 * 逐个去看:
 *      1.(形参列表)能不能简化? 因为功能接口中仅有一个必须要实现的抽象方法,所以形参的数据类型是可以省略的
 *          但是形参的名字是不可省略的(因为要在方法体中用)
 *          特殊的,如果形参只有一个,那么()也是可以省略的
 *          需要注意的是如果形参为空,()是不能省略的
 *
 *
 *     2.{
 *          //方法体
 *       }
 *       方法体是否可以简化?
 *       如果方法重写的方法体只有一条语句的话,那么可以省略大括号
 *       特殊的,如果该条语句是返回值语句,那么大括号和return可以一起省略
 *
 *
 *    3.实际上在多数情况下,都不太可能一句话把方法体写完,多数情况下,lambda表达式的抽象方法的实现都会很复杂.所以lambda表达式的抽象方法的实现可以直接指向一个已经存在的方法,而不是自己书写实现,这种语法在Java中称之为"方法引用"
 *    lambda表达式指向已实现方法的语法:
 *      (形参列表) -> 已实现的方法(形参列表);
 *      如果是同类中直接写方法名即可
 *      如果不是同类中需要带上相应的类名(对象)
 *
 * 以上形式的方法引用仍然可以继续简化:
 *      形参列表实际上是多余的,可以去掉的
 *      语法:
 *          功能接口 引用名 = 方法的归属者::方法名;
 *      方法分两类:
 *          1.静态方法,属于类,如果是静态方法,需要写类名::
 *          2.成员方法,属于对象,如果是成员方法,需要写对象名::
 *
 * 总结,什么样的方法能够作为方法引用指向的功能接口中抽象方法的实现?只看两点:
 *      1.返回值类型必须一致
 *      2.形参列表中的数量,类型,位置必须都对应上,形参名字无所谓
 *
 *
 * lambda表达式需要注意的事项:
 *      局部内部类,匿名内部类它们都有自己独立的作用域,能够自定义成员.但是lambda表达式没有自身独立的作用域,不能自定义成员,并且和方法共用同一个作用域
 *
 *
 */
public class Demo {

    public static void main(String[] args) {

        INoReturnOneParam ip = a -> System.out.println("a的值是:" + a);
        ip.test(666);

        INoReturnNoParam ip1 = () -> System.out.println("hello world!");
        ip1.test();

        INoReturnTwoParam ip2 = (a, b) -> System.out.println("a + b=" + (a + b));
        ip2.test(1, 1);

        IHasReturnNoParam ip3 = () -> 666;
        System.out.println(ip3.test());

        Student s1 = new Student(18, "张三", 100);
        Student s2 = new Student(8, "李四", 120);
        Student s3 = new Student(9, "王二", 150);
        Student s4 = new Student(29, "麻子", 0);
        Student s5 = new Student(30, "老王", 99);

        Object[] objs = {s1, s1, s3, s4, s5};
        //Variable 'arr' is already defined in the scope
        /*TraverseInterface ti = arr -> {
            for (Object o : arr) {
                if (o instanceof Student) {
                    Student s = (Student) o;
                    s.print();
                }
            }
        };
        ti.traverseObjectArr(objs);*/
        //简化
        Demo d = new Demo();
        TraverseInterface ti = arr -> d.myTraverseStuArr(arr);
        ti.traverseObjectArr(objs);
        System.out.println("-----------------");

        //继续简化
        TraverseInterface ti2 = new Demo()::myTraverseStuArr;
        ti2.traverseObjectArr(objs);

        //当然方法引用也可以指向已实现的jdk中的方法
        INoReturnNoParam ip4 = System.out::println;
        ip4.test();
        System.out.println("xxxxx");
        INoReturnOneParam ip5 = System.out::println;
        ip5.test(100);
    }

    //根据功能接口中的抽象方法,写一个它的实现方法,用于遍历Student数组
    public void myTraverseStuArr(Object[] arr2) {
        //遍历对象数组,然后强转
        for (Object o : arr2) {
            if (o instanceof Student) {
                Student s = (Student) o;
                s.print();
            }
        }
    }


}

//定义一个功能接口,该接口用于遍历打印对象数组
@FunctionalInterface
interface TraverseInterface {
    void traverseObjectArr(Object[] arr);
}

class Student {
    int age;
    String name;
    double score;

    //新建方法,用于打印学生信息
    public void print() {
        System.out.println("姓名为:" + name + "的学生,年龄是:" + age + ",考了" + score + "分!");
    }

    public Student() {
    }

    public Student(int age, String name, double score) {
        this.age = age;
        this.name = name;
        this.score = score;
    }
}

//无返回值无参数的功能接口
@FunctionalInterface
interface INoReturnNoParam {
    void test();
}

//无返回值有一个参数的功能接口
@FunctionalInterface
interface INoReturnOneParam {
    void test(int a);
}

//无返回值两个参数的功能接口
@FunctionalInterface
interface INoReturnTwoParam {
    void test(int a, int b);
}

//有返回值无参数的功能接口
@FunctionalInterface
interface IHasReturnNoParam {
    int test();
}

//有返回值一个参数的功能接口
@FunctionalInterface
interface IHasReturnOneParam {
    int method(int a);
}

//有返回值两个参数的功能接口
@FunctionalInterface
interface IHasReturnTwoParam {
    int test(int a, int b);
}